#include "myScreen.h"
#include "myTime.h"
#include <ESP8266_Heweather.h>  // 天气库
#include <TJpg_Decoder.h>
#include <JPEGDecoder.h>
#include <Ticker.h>

// 动画帧
#include "img/pangzi/i0.h"
#include "img/pangzi/i1.h"
#include "img/pangzi/i2.h"
#include "img/pangzi/i3.h"
#include "img/pangzi/i4.h"
#include "img/pangzi/i5.h"
#include "img/pangzi/i6.h"
#include "img/pangzi/i7.h"
#include "img/pangzi/i8.h"

// 图片
#include "img/wifi.h"
#include "img/wifi_offline.h"
#include "img/thermometer.h"
#include "img/water_droplet.h"

// 字体
#include "font/STKAITI20.h"
#include "font/ARLRDBD60.h"
#include "font/ARLRDBD40.h"

#define ROTATION_DIR 1  // 显示屏方向
#define minimum(a, b) (((a) < (b)) ? (a) : (b))

// 屏幕以及精灵图
TFT_eSPI tft = TFT_eSPI();
TFT_eSprite clk = TFT_eSprite(&tft);

void my_screen_initial() {
  // 初始化屏幕
  tft.init();
  tft.setRotation(ROTATION_DIR);

  // 显示加载进度条
  tft.fillScreen(TFT_BLACK);
}


uint8_t popup_sprite_w = 120;
uint8_t popup_sprite_h = 30;
uint8_t popup_sprite_x = 75;
uint8_t popup_sprite_y = 10;

int16_t popup_rect_y = -popup_sprite_h;
int16_t popup_rect_y_top = -popup_sprite_h;
uint8_t popup_rect_y_bottom = 0;

uint8_t popup_step = 0;
String popup_content = "这是个弹窗";
Ticker popup_change;

void popup_move() {
  // 下行
  if (popup_step == 0) {
    if (popup_rect_y < popup_rect_y_bottom) {
      popup_rect_y += 1;
      popup_change.once_ms(50, popup_move);
    } else {
      popup_change.detach();
      popup_step = 1;
      popup_change.once(2, popup_move);
    }
  }
  // 转上行
  else if (popup_step == 1) {
    if (popup_rect_y > popup_rect_y_top) {
      popup_rect_y -= 1;
      popup_change.once_ms(50, popup_move);
    } else {
      popup_change.detach();
      popup_step = 0;
      popup_change.once(2, popup_move);
    }
  }
}

void activate_popup(const char *str) {
  popup_content = String(str);
  popup_change.once_ms(50, popup_move);
}

// 显示浮框
void show_popup() {
  clk.createSprite(popup_sprite_w, popup_sprite_h);
  clk.fillSprite(TFT_BLACK);
  clk.fillRoundRect(0, popup_rect_y, popup_sprite_w, popup_sprite_h, 3, TFT_SKYBLUE);

  clk.loadFont(STKAITI20);
  clk.setTextDatum(CC_DATUM);
  clk.setTextColor(TFT_BLACK, TFT_BLACK);
  clk.drawString(popup_content, popup_sprite_w / 2, popup_rect_y + popup_sprite_h / 2, 2);

  clk.pushSprite(popup_sprite_x, popup_sprite_y);
  clk.unloadFont();
  clk.deleteSprite();
}

// 加载画面
byte loadNum = 1;
byte COLOR_DEPTH = 8;
uint8_t PROGRESS_AREA_WIDTH = 220;
uint8_t PROGRESS_AREA_HEIGHT = 50;
uint8_t PROGRESS_AREA_X = (240 - PROGRESS_AREA_WIDTH) / 2;
uint8_t PROGRESS_AREA_Y = 110;
uint8_t PROGRESS_BAR_X = 0;
uint8_t PROGRESS_BAR_Y = 0;
uint8_t PROGRESS_BAR_WIDTH = 220;
uint8_t PROGRESS_BAR_HEIGHT = 16;
uint8_t PROGRESS_BAR_RAD = 8;
uint8_t PROGRESS_GAP_WIDTH = 3;
uint8_t PROGRESS_LINE_RAD = 5;
uint8_t PROGRESS_TXT_X = PROGRESS_AREA_WIDTH / 2;
uint8_t PROGRESS_TXT_Y = 40;
uint8_t PROGRESS_TXT_FONT = 2;
uint8_t PROGRESS_STEP = 0;
String PROGRESS_TXT[] = { "网络连接中...", "初始化时间中...", "配置天气中..." };

void my_screen_initial_next_step() {
  PROGRESS_STEP += 1;
}

void my_screen_initial_go_done() {
  while (loadNum < PROGRESS_AREA_WIDTH - 2 * PROGRESS_GAP_WIDTH) {  // 让动画走完
    loading(1);
  }

  // 清屏
  tft.fillScreen(TFT_BLACK);
}

void loading(byte delayTime) {
  clk.setColorDepth(COLOR_DEPTH);
  // 加载中文汉字
  clk.loadFont(STKAITI20);

  clk.createSprite(PROGRESS_AREA_WIDTH, PROGRESS_AREA_HEIGHT);
  clk.fillSprite(TFT_BLACK);

  clk.drawRoundRect(PROGRESS_BAR_X, PROGRESS_BAR_Y, PROGRESS_BAR_WIDTH, PROGRESS_BAR_HEIGHT, PROGRESS_BAR_RAD, TFT_WHITE);
  clk.fillRoundRect(PROGRESS_GAP_WIDTH, PROGRESS_GAP_WIDTH, loadNum, PROGRESS_BAR_HEIGHT - 2 * PROGRESS_GAP_WIDTH, PROGRESS_LINE_RAD, TFT_WHITE);

  clk.setTextDatum(CC_DATUM);
  clk.setTextColor(TFT_GREEN, TFT_BLACK);
  clk.drawString(PROGRESS_TXT[PROGRESS_STEP], PROGRESS_TXT_X, PROGRESS_TXT_Y, PROGRESS_TXT_FONT);

  clk.pushSprite(PROGRESS_AREA_X, PROGRESS_AREA_Y);
  clk.deleteSprite();

  if (loadNum >= PROGRESS_AREA_WIDTH / 3 && PROGRESS_STEP == 0) {
    loadNum += 0;
  } else if (loadNum >= PROGRESS_AREA_WIDTH - 2 * PROGRESS_GAP_WIDTH) {
    loadNum = PROGRESS_AREA_WIDTH - 2 * PROGRESS_GAP_WIDTH;
  } else {
    loadNum += 1;
  }
  delay(delayTime);

  clk.unloadFont();
}

// 展示天气详细信息（滚动栏）
const uint8_t msgs_len = 5;
String weather_messages[msgs_len];
uint8_t msgs_strlen[msgs_len];
extern WeatherNow weatherNow;
extern WeatherForecast WeatherForecast;


void make_weather_txt() {
  weather_messages[0] = "今日天气：" + weatherNow.getWeatherText();
  if ((int)weatherNow.getPrecip() == 0) {
    weather_messages[1] = "此刻无降水。";
  } else {
    weather_messages[1] = "实况降水：" + String(weatherNow.getPrecip()) + "毫米";
  }
  weather_messages[2] = "实况风向：" + String(weatherNow.getWindDir());
  weather_messages[3] = "实况风速：" + String(weatherNow.getWindScale()) + "级";
  weather_messages[4] = "今日最高温：" + String(WeatherForecast.getTempMax(0)) + "℃ " + "今日最低温：" + String(WeatherForecast.getTempMin(0)) + "℃";

  for (uint8_t i = 0; i < msgs_len; i++) {
    msgs_strlen[i] = utf8_strlen(weather_messages[i].c_str());
  }
}

// 函数计算UTF-8编码字符串的字符数
size_t utf8_strlen(const char *str) {
  size_t length = 0;
  const unsigned char *uchar_str = (const unsigned char *)str;

  while (*uchar_str) {
    if ((*uchar_str & 0xc0) != 0x80) {  // 如果是UTF-8字符的开始字节
      length++;
    }
    uchar_str++;
  }

  return length;
}

uint8_t weather_msg_w = 200;  // 超出边界，方便字幕滑动
uint8_t weather_msg_h = 20;
uint8_t weather_msg_x = 103;
uint8_t weather_msg_y = 210;
int16_t weather_txt_x = -20 * 5;
uint8_t weather_txt_y = weather_msg_h / 2;
uint8_t curr_msg_idx = 0;

void show_weather_scroll() {
  clk.createSprite(weather_msg_w, weather_msg_h);
  clk.fillSprite(TFT_BLACK);

  clk.loadFont(STKAITI20);
  clk.setTextDatum(ML_DATUM);
  clk.setTextColor(TFT_WHITE, TFT_WHITE);
  clk.drawString(weather_messages[curr_msg_idx], -weather_txt_x, weather_txt_y, 1);
  clk.unloadFont();

  clk.pushSprite(weather_msg_x, weather_msg_y);
  clk.deleteSprite();
}

uint8_t font_size = 20;
uint8_t wea_txt_step = 1;
uint8_t intial_font_num = 6;

void weather_scroll_move() {
  weather_txt_x += wea_txt_step;
  if (weather_txt_x >= msgs_strlen[curr_msg_idx] * font_size) {
    curr_msg_idx += 1;
    curr_msg_idx %= msgs_len;
    weather_txt_x = -font_size * intial_font_num;
  }
}

// 展示湿度
uint8_t humidity_gImage_droplet_x = 103;
uint8_t humidity_gImage_droplet_y = 170;
uint8_t humidity_gImage_droplet_width = 30;

uint8_t humidity_bar_sprite_x = humidity_gImage_droplet_x + 32;
uint8_t humidity_bar_sprite_y = humidity_gImage_droplet_y;
uint8_t humidity_bar_sprite_width = 100;
uint8_t humidity_bar_sprite_height = humidity_gImage_droplet_width;

uint8_t humidity_top = 70;
uint8_t humidity_bar_length = 50;
uint8_t humidity_bar_height = 8;
uint8_t humidity_bar_gap = 3;
uint8_t humidity_bar_x = 0;
uint8_t humidity_bar_y = (humidity_gImage_droplet_width - humidity_bar_height) / 2;

uint8_t humidity_line_x = humidity_bar_gap;
uint8_t humidity_line_y = humidity_bar_y + humidity_bar_gap;
uint8_t humidity_line_height = humidity_bar_height - 2 * humidity_bar_gap;

uint8_t humidity_txt_x = humidity_bar_length + 25;
uint8_t humidity_txt_y = humidity_gImage_droplet_width / 2;

void show_humidity() {
  // 湿度图标
  drawArrayJpeg(gImage_water_droplet, sizeof(gImage_water_droplet), humidity_gImage_droplet_x, humidity_gImage_droplet_y);

  clk.createSprite(humidity_bar_sprite_width, humidity_bar_sprite_height);
  clk.fillSprite(TFT_BLACK);

  float humidityNum = weatherNow.getHumidity();
  float compute_humidityNum = humidityNum;
  if (compute_humidityNum > humidity_top) {
    compute_humidityNum = humidity_top;
  }
  int humidityPerc = compute_humidityNum / humidity_top * (humidity_bar_length - 2 * humidity_bar_gap);

  clk.drawRoundRect(humidity_bar_x, humidity_bar_y, humidity_bar_length, humidity_bar_height, 5, TFT_SKYBLUE);
  clk.fillRoundRect(humidity_line_x, humidity_line_y, humidityPerc, humidity_line_height, 3, TFT_SKYBLUE);

  clk.loadFont(STKAITI20);
  clk.setTextDatum(CC_DATUM);
  clk.setTextColor(TFT_WHITE, TFT_WHITE);
  clk.drawString(String((int)humidityNum) + "%", humidity_txt_x, humidity_txt_y, 1);
  clk.unloadFont();

  clk.pushSprite(humidity_bar_sprite_x, humidity_bar_sprite_y);
  clk.deleteSprite();
}

// 展示温度
uint8_t temp_gImage_thermometer_x = 103;
uint8_t temp_gImage_thermometer_y = 135;
uint8_t temp_gImage_thermometer_width = 30;

uint8_t temp_bar_sprite_x = temp_gImage_thermometer_x + 32;
uint8_t temp_bar_sprite_y = temp_gImage_thermometer_y;

uint8_t temp_bar_gap = 3;
uint8_t temp_top = 40;
uint8_t temp_bar_length = 50;
uint8_t temp_bar_height = 8;
uint8_t temp_bar_x = 0;
uint8_t temp_bar_y = (temp_gImage_thermometer_width - temp_bar_height) / 2;
uint8_t temp_bar_sprite_width = 100;
uint8_t temp_bar_sprite_height = temp_gImage_thermometer_width;

uint8_t temp_line_x = temp_bar_gap;
uint8_t temp_line_y = temp_bar_y + temp_bar_gap;
uint8_t temp_line_height = temp_bar_height - 2 * temp_bar_gap;

uint8_t temp_txt_x = temp_bar_length + 25;
uint8_t temp_txt_y = temp_gImage_thermometer_width / 2;

void show_temp() {
  // 温度
  drawArrayJpeg(gImage_thermometer, sizeof(gImage_thermometer), temp_gImage_thermometer_x, temp_gImage_thermometer_y);

  clk.createSprite(temp_bar_sprite_width, temp_bar_sprite_height);
  clk.fillSprite(TFT_BLACK);

  float tempNum = weatherNow.getTemp();
  float compute_tempNum = tempNum;
  if (compute_tempNum > temp_top) {
    compute_tempNum = temp_top;
  } else if (compute_tempNum < 0) {
    compute_tempNum = 0;
  }
  int tempPerc = compute_tempNum / temp_top * (temp_bar_length - 2 * temp_bar_gap);

  clk.drawRoundRect(temp_bar_x, temp_bar_y, temp_bar_length, temp_bar_height, 5, TFT_ORANGE);
  clk.fillRoundRect(temp_line_x, temp_line_y, tempPerc, temp_line_height, 3, TFT_ORANGE);

  clk.loadFont(STKAITI20);
  clk.setTextDatum(CC_DATUM);
  clk.setTextColor(TFT_WHITE, TFT_WHITE);
  clk.drawString(String((int)tempNum) + "℃", temp_txt_x, temp_txt_y, 1);
  clk.unloadFont();

  clk.pushSprite(temp_bar_sprite_x, temp_bar_sprite_y);
  clk.deleteSprite();
}

// 展示日历
uint8_t calender_width = 240;
uint8_t calender_height = 20;
uint8_t calender_x = 0;
uint8_t calender_y = 110;
uint8_t calender_rect_width = 210;
uint8_t calender_rect_height = 20;
uint8_t calender_rect_rad = 8;
uint8_t calender_rect_x = (calender_width - calender_rect_width) / 2;
uint8_t calender_rect_y = 0;
uint8_t calender_txt_x = calender_width / 2;
uint8_t calender_txt_y = calender_height / 2 + 2;
extern CurrentTime mytime;

void show_calender() {
  clk.createSprite(calender_width, calender_height);
  clk.fillSprite(TFT_BLACK);
  clk.fillRoundRect(calender_rect_x, 0, calender_rect_width, calender_rect_height, calender_rect_rad, tft.color565(68, 114, 196));

  clk.loadFont(STKAITI20);
  clk.setTextDatum(CC_DATUM);
  clk.setTextColor(TFT_WHITE, TFT_WHITE);
  clk.drawString(mytime.get_calender_str(), calender_txt_x, calender_txt_y);

  clk.pushSprite(calender_x, calender_y);
  clk.unloadFont();
  clk.deleteSprite();
}

// 展示城市
void show_city() {
  clk.createSprite(50, 30);
  clk.fillSprite(TFT_BLACK);
  clk.fillRoundRect(0, 0, 50, 30, 5, tft.color565(80, 172, 164));

  clk.loadFont(STKAITI20);
  clk.setTextDatum(CC_DATUM);
  clk.setTextColor(TFT_BLACK, TFT_BLACK);
  clk.drawString("兴宁", 25, 15, 2);

  clk.pushSprite(20, 10);
  clk.unloadFont();
  clk.deleteSprite();
}

// 太空人动画帧
uint8_t pangzi_step = 0, pangzi_step_befo = 0;
const uint8_t *gif_pangzi_frames[] PROGMEM = { gif_pangzi_0, gif_pangzi_1, gif_pangzi_2, gif_pangzi_3, gif_pangzi_4, gif_pangzi_5, gif_pangzi_6, gif_pangzi_7, gif_pangzi_8 };
const int gif_pangzi_sizes[] PROGMEM = { 3459, 3567, 3354, 3458, 3385, 3371, 3413, 3251, 3230 };
// 帧下一步
void gif_pangzi_next_step() {
  pangzi_step += 1;
  pangzi_step %= 9;
}
// 太空人gif展示
void show_gif_pangzi() {
  drawArrayJpeg(gif_pangzi_frames[pangzi_step], gif_pangzi_sizes[pangzi_step], 0, 140);
}

// 时钟展示
void show_clock() {
  // 时
  clk.createSprite(80, 60);
  clk.fillSprite(TFT_BLACK);
  // clk.fillRoundRect(0, 0, 80, 60, 5, TFT_WHITE);

  clk.loadFont(ARLRDBD60);
  clk.setTextDatum(CC_DATUM);
  clk.setTextColor(tft.color565(112, 173, 71), tft.color565(112, 173, 71));
  clk.drawString(mytime.get_hour_str(), 40, 30, 1);

  clk.pushSprite(0, 50);
  clk.unloadFont();
  clk.deleteSprite();

  // 分
  clk.createSprite(80, 60);
  clk.fillSprite(TFT_BLACK);

  clk.loadFont(ARLRDBD60);
  clk.setTextDatum(CC_DATUM);
  clk.setTextColor(tft.color565(91, 155, 213), tft.color565(91, 155, 213));
  clk.drawString(mytime.get_minute_str(), 40, 30, 1);

  clk.pushSprite(80, 50);
  clk.unloadFont();
  clk.deleteSprite();

  // 秒
  clk.createSprite(60, 40);
  clk.fillSprite(TFT_BLACK);

  clk.loadFont(ARLRDBD40);
  clk.setTextDatum(CC_DATUM);
  clk.setTextColor(tft.color565(237, 125, 49), tft.color565(237, 125, 49));
  clk.drawString(mytime.get_second_str(), 30, 20, 1);

  clk.pushSprite(160, 65);
  clk.unloadFont();
  clk.deleteSprite();
}

// 检查是否连上WIFI
wl_status_t wifi_prev_state;
wl_status_t wifi_curr_state;
uint8_t gImage_wifi_x = 200;
uint8_t gImage_wifi_y = 10;
void check_WiFi() {
  wifi_prev_state = wifi_curr_state;
  wifi_curr_state = WiFi.status();

  if (wifi_prev_state != wifi_curr_state) {
    if (WiFi.status() == WL_CONNECTED)
      drawArrayJpeg(gImage_wifi_online, sizeof(gImage_wifi_online), gImage_wifi_x, gImage_wifi_y);
    else
      drawArrayJpeg(gImage_wifi_offline, sizeof(gImage_wifi_offline), gImage_wifi_x, gImage_wifi_y);
  }
}

// ####################################################################################################
//  Draw a JPEG on the TFT pulled from a program memory array
// ####################################################################################################
void drawArrayJpeg(const uint8_t arrayname[], uint32_t array_size, int xpos, int ypos) {

  int x = xpos;
  int y = ypos;

  JpegDec.decodeArray(arrayname, array_size);

  // jpegInfo(); // Print information from the JPEG file (could comment this line out)

  renderJPEG(x, y);

  // Serial.println("#########################");
}

// ####################################################################################################
//  Draw a JPEG on the TFT, images will be cropped on the right/bottom sides if they do not fit
// ####################################################################################################
//  This function assumes xpos,ypos is a valid screen coordinate. For convenience images that do not
//  fit totally on the screen are cropped to the nearest MCU size and may leave right/bottom borders.
void renderJPEG(int xpos, int ypos) {

  // retrieve information about the image
  uint16_t *pImg;
  uint16_t mcu_w = JpegDec.MCUWidth;
  uint16_t mcu_h = JpegDec.MCUHeight;
  uint32_t max_x = JpegDec.width;
  uint32_t max_y = JpegDec.height;

  // Jpeg images are draw as a set of image block (tiles) called Minimum Coding Units (MCUs)
  // Typically these MCUs are 16x16 pixel blocks
  // Determine the width and height of the right and bottom edge image blocks
  uint32_t min_w = minimum(mcu_w, max_x % mcu_w);
  uint32_t min_h = minimum(mcu_h, max_y % mcu_h);

  // save the current image block size
  uint32_t win_w = mcu_w;
  uint32_t win_h = mcu_h;

  // record the current time so we can measure how long it takes to draw an image
  uint32_t drawTime = millis();

  // save the coordinate of the right and bottom edges to assist image cropping
  // to the screen size
  max_x += xpos;
  max_y += ypos;

  // read each MCU block until there are no more
  while (JpegDec.readSwappedBytes()) {

    // save a pointer to the image block
    pImg = JpegDec.pImage;

    // calculate where the image block should be drawn on the screen
    int mcu_x = JpegDec.MCUx * mcu_w + xpos;  // Calculate coordinates of top left corner of current MCU
    int mcu_y = JpegDec.MCUy * mcu_h + ypos;

    // check if the image block size needs to be changed for the right edge
    if (mcu_x + mcu_w <= max_x)
      win_w = mcu_w;
    else
      win_w = min_w;

    // check if the image block size needs to be changed for the bottom edge
    if (mcu_y + mcu_h <= max_y)
      win_h = mcu_h;
    else
      win_h = min_h;

    // copy pixels into a contiguous block
    if (win_w != mcu_w) {
      uint16_t *cImg;
      int p = 0;
      cImg = pImg + win_w;
      for (int h = 1; h < win_h; h++) {
        p += mcu_w;
        for (int w = 0; w < win_w; w++) {
          *cImg = *(pImg + w + p);
          cImg++;
        }
      }
    }

    // draw image MCU block only if it will fit on the screen
    if ((mcu_x + win_w) <= tft.width() && (mcu_y + win_h) <= tft.height()) {
      tft.pushRect(mcu_x, mcu_y, win_w, win_h, pImg);
    } else if ((mcu_y + win_h) >= tft.height())
      JpegDec.abort();  // Image has run off bottom of screen so abort decoding
  }

  // calculate how long it took to draw the image
  drawTime = millis() - drawTime;

  // print the results to the serial port
  // Serial.print(F(  "Total render time was    : ")); Serial.print(drawTime); Serial.println(F(" ms"));
  // Serial.println(F(""));
}

// ####################################################################################################
//  Print image information to the serial port (optional)
// ####################################################################################################
void jpegInfo() {
  Serial.println(F("==============="));
  Serial.println(F("JPEG image info"));
  Serial.println(F("==============="));
  Serial.print(F("Width      :"));
  Serial.println(JpegDec.width);
  Serial.print(F("Height     :"));
  Serial.println(JpegDec.height);
  Serial.print(F("Components :"));
  Serial.println(JpegDec.comps);
  Serial.print(F("MCU / row  :"));
  Serial.println(JpegDec.MCUSPerRow);
  Serial.print(F("MCU / col  :"));
  Serial.println(JpegDec.MCUSPerCol);
  Serial.print(F("Scan type  :"));
  Serial.println(JpegDec.scanType);
  Serial.print(F("MCU width  :"));
  Serial.println(JpegDec.MCUWidth);
  Serial.print(F("MCU height :"));
  Serial.println(JpegDec.MCUHeight);
  Serial.println(F("==============="));
}

// ####################################################################################################
//  Show the execution time (optional)
// ####################################################################################################
//  WARNING: for UNO/AVR legacy reasons printing text to the screen with the Mega might not work for
//  sketch sizes greater than ~70KBytes because 16-bit address pointers are used in some libraries.

// The Due will work fine with the HX8357_Due library.

void showTime(uint32_t msTime) {
  // tft.setCursor(0, 0);
  // tft.setTextFont(1);
  // tft.setTextSize(2);
  // tft.setTextColor(TFT_WHITE, TFT_BLACK);
  // tft.print(F(" JPEG drawn in "));
  // tft.print(msTime);
  // tft.println(F(" ms "));
  Serial.print(F(" JPEG drawn in "));
  Serial.print(msTime);
  Serial.println(F(" ms "));
}